package bma.common.thrift.xdom;

import java.util.Iterator;

import org.apache.thrift.TException;
import org.apache.thrift.protocol.TField;
import org.apache.thrift.protocol.TList;
import org.apache.thrift.protocol.TMap;
import org.apache.thrift.protocol.TProtocol;
import org.apache.thrift.protocol.TSet;
import org.apache.thrift.protocol.TStruct;
import org.apache.thrift.protocol.TType;

import bma.common.langutil.xdom.Xdom;

public class Writer {

	private DynamicThriftManager manager;

	public Writer(DynamicThriftManager manager) {
		super();
		this.manager = manager;
	}

	public DynamicThriftManager getManager() {
		return manager;
	}

	public void setManager(DynamicThriftManager manager) {
		this.manager = manager;
	}

	public void writeStruct(TDynamicStruct struct, Xdom dom, TProtocol out)
			throws TException {
		if (!dom.isMap())
			return;

		TStruct s = new TStruct(struct.getName());
		out.writeStructBegin(s);
		Iterator<String> it = dom.mapNames();
		while (it.hasNext()) {
			String name = it.next();
			TDynamicField dfield = struct.getField(name);
			if (dfield == null)
				continue;
			if (dfield.getTypeValue() == TType.VOID)
				continue;

			Xdom val = dom.get(name);
			TField schemeField = new TField(dfield.getName(),
					dfield.getTypeValue(), (short) dfield.getKey());
			out.writeFieldBegin(schemeField);
			write(dfield.getType(), val, out);
			out.writeFieldEnd();
		}
		out.writeFieldStop();
		out.writeStructEnd();
	}

	public void write(TDynamicType type, Xdom val, TProtocol out)
			throws TException {
		switch (type.getType()) {
		case TType.VOID:
			return;
		case TType.BOOL:
			out.writeBool(val.getBooleanValue());
			return;
		case TType.BYTE:
			Number v8 = val.getNumberValue();
			out.writeByte(v8 != null ? v8.byteValue() : 0);
			return;
		case TType.DOUBLE:
			out.writeDouble(val.getDoubleValue());
			return;
		case TType.I16:
			Number v16 = val.getNumberValue();
			out.writeI16(v16 != null ? v16.shortValue() : 0);
			return;
		case TType.I32:
			out.writeI32(val.getIntValue());
			return;
		case TType.I64:
			out.writeI64(val.getLongValue());
			return;
		case TType.STRING:
			String vs = val.getStringValue();
			out.writeString(vs == null ? "" : vs);
			return;
		case TType.STRUCT: {
			TDynamicStruct struct = manager.getStruct(type.getName());
			writeStruct(struct, val, out);
			return;
		}
		case TType.MAP: {
			TDynamicType keyType = type.getKeyType();
			TDynamicType innerType = type.getInnerType();
			TMap map = new TMap(keyType.getType(), innerType.getType(),
					val.isArray() ? val.size() : 0);
			out.writeMapBegin(map);
			Iterator<Xdom> it = val.arrayIterator();
			if (it != null) {
				while (it.hasNext()) {
					Xdom e = it.next();
					Xdom key = e.get("key");
					Xdom v = e.get("value");
					if (key != null && val != null) {
						write(keyType, key, out);
						write(innerType, v, out);
					}
				}
			}
			out.writeMapEnd();
			return;
		}
		case TType.SET: {
			TDynamicType innerType = type.getInnerType();
			TSet set = new TSet(innerType.getType(), val.isArray() ? val.size()
					: 0);
			out.writeSetBegin(set);
			Iterator<Xdom> it = val.arrayIterator();
			if (it != null) {
				while (it.hasNext()) {
					Xdom v = it.next();
					write(innerType, v, out);
				}
			}
			out.writeSetEnd();
			return;
		}
		case TType.LIST: {
			TDynamicType innerType = type.getInnerType();
			TList list = new TList(innerType.getType(),
					val.isArray() ? val.size() : 0);
			out.writeListBegin(list);
			Iterator<Xdom> it = val.arrayIterator();
			if (it != null) {
				while (it.hasNext()) {
					Xdom v = it.next();
					write(innerType, v, out);
				}
			}
			out.writeListEnd();
			return;
		}
		case TType.ENUM:
			if (val.isString()) {
				int v = manager.getEnumValue(type.getName(),
						val.getStringValue());
				out.writeI32(v);
			} else {
				out.writeI32(val.getIntValue());
			}
			return;
		}
		return;
	}

}
